package org.ccay.upload.impl;

import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.servlet.ServletRequestContext;
import org.ccay.audit.service.ILogService;
import org.ccay.audit.vo.LogVO;
import org.ccay.core.exception.ApplicationException;
import org.ccay.core.exception.ExceptionHandler;
import org.ccay.core.io.FileStoreType;
import org.ccay.core.io.VFSFileObject;
import org.ccay.core.log.CcayLoggerFactory;
import org.ccay.core.log.ILogger;
import org.ccay.core.request.impl.RequestContext;
import org.ccay.core.util.CollectionUtil;
import org.ccay.core.util.FileUtil;
import org.ccay.core.util.JsonUtil;
import org.ccay.core.util.PathUtil;
import org.ccay.core.util.StringUtil;
import org.ccay.registry.util.RegistryQueryUtil;
import org.ccay.upload.FolderSplitType;
import org.ccay.upload.IUploadFileConsumer;
import org.ccay.upload.IUploadSupportService;
import org.ccay.upload.UploadException;
import org.ccay.upload.UploadSettingVO;
import org.ccay.vfs.ApacheVFSFileObject;
import org.ccay.vfs.VFS;
import org.ccay.web.support.internal.impl.RequestUtil;

@Named("/servlet/upload")
public class UploadServlet extends HttpServlet {
	private static final String UPLOAD_ERROR = "ccay.upload.support.00010001";
	private static final int FORM_BUFFER_SIZE = 2000;
	private static ILogger log = CcayLoggerFactory.getLogger(UploadServlet.class);

	private static final long serialVersionUID = -460601782542501990L;

	@Inject
	private IUploadSupportService uploadSupportService;

	@Inject
	private ILogService logService;

	public UploadServlet() {
	}

	public void init(ServletConfig servletConfig) throws ServletException {
	}

	public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
		HttpServletRequest httpRequest = (HttpServletRequest) request;

		log.debug("Upload Query String:{0}", new Object[] { httpRequest.getQueryString() });

		String uploadType = request.getParameter("ulType");

		Map<String, String> queryParams = RequestUtil.getRequestQueryParams(httpRequest);
		try {
			Map<String, String> convertedMap = CollectionUtil.convert(request.getParameterMap());

			convertedMap.put("taskType", queryParams.get("taskType"));
			Object obj = processUpload(httpRequest, uploadType, convertedMap);

			String json = JsonUtil.getJsonString(obj);

			log.debug("Object return:{0}", new Object[] { json });

			response.setContentType("text/html");
			response.getWriter().write(json);
		} catch (UploadException e) {
			log.error(e);
			throw new ServletException(e);
		} catch (ApplicationException e) {
			log.error(e);
			throw new ServletException(new UploadException(UPLOAD_ERROR, e));
		} catch (FileUploadException e) {
			log.error(e);
			throw new ServletException(new UploadException(UPLOAD_ERROR, e, ExceptionHandler.getFriendlyFault(e).getMessage()));
		} catch (RuntimeException e) {
			log.error(e);
			throw new ServletException(new UploadException(UPLOAD_ERROR, e, ExceptionHandler.getFriendlyFault(e).getMessage()));
		}
	}

	private Object processUpload(HttpServletRequest request, String uploadType, Map<String, String> parameters) throws ApplicationException, FileUploadException {
		IUploadFileConsumer uls = this.uploadSupportService.findUploadConsumer(uploadType);

		if (!uls.validatePrivilege(parameters)) {
			throw new UploadException("ccay.security.00010001");
		}

		DiskFileItemFactory factory = new DiskFileItemFactory();

		UploadSettingVO uploadSetting = uls.getUploadSetting(parameters);

		factory.setSizeThreshold(uploadSetting.getMaxMemory());
		String tempPath = RegistryQueryUtil.getValue(VFS.TEMP_DIR_REGISTRY_NAME);
		if(StringUtil.isNullOrEmpty(tempPath)){
			tempPath = System.getProperty("java.io.tmpdir");
		}
		File file = new File(tempPath);
		PathUtil.makeDirs(file);
		factory.setRepository(file);

		ServletFileUpload upload = new ServletFileUpload(factory);

		upload.setSizeMax(uploadSetting.getMaxFileSize() + 2000L);

		if ("HtmlAreaImage".equals(uploadType)) {
			org.apache.commons.fileupload.RequestContext rc = new ServletRequestContext(request);
			long rLength = rc.getContentLength();
			long setLength = upload.getSizeMax();
			log.debug("Upload image ok?:realLength={0},set={1}", new Object[] { Long.valueOf(rLength), Long.valueOf(setLength) });
			if (rLength > setLength) {
				throw new UploadException("ccay.upload.support.00010004", rLength + "");
			}
		}

		List<FileItem> items = upload.parseRequest(request);

		return processFileItems(parameters, uploadSetting, uls, items);
	}

	private Object processFileItems(Map<String, String> parameters, UploadSettingVO uploadSetting, IUploadFileConsumer consumer, List<FileItem> items) throws ApplicationException {
		Iterator<FileItem> iter = items.iterator();
		UploadSettingVO setting = consumer.getUploadSetting(parameters);
		List<VFSFileObject> files = new ArrayList<VFSFileObject>();
		while (iter.hasNext()) {
			FileItem item = (FileItem) iter.next();
			if (!item.isFormField()) {
				if (item.getSize() == 0L) {
					throw new UploadException("ccay.upload.support.00010003");
				}
				validateExtention(uploadSetting, item);
				String relativePath = MessageFormat.format("{0}/{1}.jlr", new Object[] {generatePathPart(uploadSetting), UUID.randomUUID().toString() });
				
				
				ApacheVFSFileObject fileObject = new ApacheVFSFileObject(setting.getVfsModule(),relativePath,FileStoreType.WROK);
				
				//采用本地文件系统操作 适配 @{link org.apache.commons.fileupload.FileItem.write(File)}
				File uploadedFile = new File(fileObject.getLocalAbsolutePath());
				
				PathUtil.makeDirs(uploadedFile.getParent());
				
				writeFile(item, uploadedFile);
				
				fileObject.setDisplayName(FileUtil.getFileName(item.getName()));
				
				files.add(fileObject);
			}
		}
		
		sendlogMessage((String) parameters.get("ulType"), files);

		return consumer.processFiles(parameters, files);
	}

	private void sendlogMessage(String type, List<VFSFileObject> files) throws ApplicationException {
		LogVO logMessage = new LogVO();
		logMessage.buildAotoAttribute();

		logMessage.setClazz("UploadServlet");

		logMessage.setOperation("Upload");
		logMessage.setLogType(0);
		StringBuffer message = new StringBuffer();
		for (VFSFileObject fileInfoVO : files) {
			message.append(", " + fileInfoVO.getDisplayName());
		}
		String mess = null;
		if (message.length() > 1) {
			mess = message.substring(1, message.length());
		}

		logMessage.setModule(type + " Upload");
		logMessage.setServerName(RequestUtil.getServerName());
		logMessage.setMessage("upload file name is " + mess);
		
		this.logService.asyncCreateLog(logMessage);
	}

	private String writeFile(FileItem item, File uploadedFile) throws UploadException {
		try {
			item.write(uploadedFile);
			return uploadedFile.getPath();
		} catch (Exception e) {
			throw new UploadException("ccay.upload.support.00010001", e);
		}
	}

	private void validateExtention(UploadSettingVO uploadSetting, FileItem item) throws UploadException {
		if (!isFileTypesAllowed(item.getName(), uploadSetting.getFileTypesAllowed())) {
			log.debug("File type {0} is not valid for {1}", new Object[] { FileUtil.getExtension(item.getName()), item.getName() });
			throw new UploadException("ccay.upload.support.00010002", StringUtil.join(uploadSetting.getFileTypesAllowed(), ","));
		}
	}

	private boolean isFileTypesAllowed(String fileName, List<String> fileTypesAllowed) {
		for (String fileType : fileTypesAllowed) {
			if (StringUtil.toLower(fileName).endsWith(StringUtil.toLower(fileType))) {
				return true;
			}
		}
		return false;
	}

	private String generatePathPart(UploadSettingVO uploadSetting) {
		if (uploadSetting.getFileSplitType().equals(FolderSplitType.ByDay)) {
			return dayFormat.format(new Date());
		}
		return RequestContext.getCurrent().getUser().getUserName();
	}

	private static SimpleDateFormat dayFormat = new SimpleDateFormat("yyyy-MM-dd");
}
